package com.yzy.fly.service.util;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.google.common.collect.Lists;
import com.yzy.fly.annotation.FlyIndex;
import com.yzy.fly.annotation.OrderSeq;
import com.yzy.fly.annotation.ParentId;
import com.yzy.fly.annotation.RoleChildren;
import com.yzy.fly.entity.role.FlyTableColumn;
import com.yzy.fly.entity.role.ParentIdColumn;
import com.yzy.fly.kernel.model.MetadataModel;
import com.yzy.fly.kernel.model.ParentIdType;
import com.yzy.fly.type.JDBCType;
import com.yzy.fly.type.JJFieldType;
import com.yzy.fly.util.SqlUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

public class MetadataModelParse {
    private MetadataModel metadataModel;

    public MetadataModelParse(MetadataModel metadataModel){
        this.metadataModel = metadataModel;
    }

    public void exec(Object object){
        Class<?> cls = object.getClass();
        exec(cls);
    }

    public void exec(Class cls){
        String className = cls.getName();
        String tableName = getTableName(cls);
        parseIdColumn(metadataModel, cls);
        String parentIdColumName = getParentIdColumName(cls);
        String seqColumnName = getSeqColumnName(cls);
        Boolean isAutoResultMap = isAutoResultMap(cls);
        metadataModel.setClassName(className);
        metadataModel.setClassSimpleName(cls.getSimpleName());
        metadataModel.setCls(cls);
        metadataModel.setTableName(tableName);
        metadataModel.setSeqColumnName(seqColumnName);
        metadataModel.setParentIdColumnName(parentIdColumName);
        metadataModel.setIsAutoResultMap(isAutoResultMap);

        getParentIdColumNames(cls,metadataModel);
        parseColumNames(cls,metadataModel);
//        parseRelated(cls,metadataModel);
    }

    private String getTableName(Class<?> cls){
        String tableName = "";
        if (cls.isAnnotationPresent(TableName.class)) {
            TableName annotation = cls.getAnnotation(TableName.class);
            tableName = annotation.value();
        }
        return tableName;
    }

    private String getSeqColumnName(Class<?> cls){
        String columnName = "";
        List<Field> orderSeqFields = FieldInvokeUtil.findFieldsByAnnotationPresent(cls, OrderSeq.class);
        if (CollectionUtils.isNotEmpty(orderSeqFields)) {
            Field field = orderSeqFields.get(0);
            boolean annotationTableFieldPresent = field.isAnnotationPresent(TableField.class);
            if (annotationTableFieldPresent) {
                TableField tableFieldAnn = field.getAnnotation(TableField.class);
                if (tableFieldAnn.exist()) {
                    columnName = tableFieldAnn.value();
                }
            }
            if (StringUtils.isBlank(columnName)) {
                columnName = field.getName();
            }
        }
        return columnName;
    }

    private void parseIdColumn(MetadataModel metadataModel, Class<?> cls){
        IdType idType = null;
        String columnName = "";
        List<Field> fields = FieldInvokeUtil.findFieldsByAnnotationPresent(cls, TableId.class);
        if (CollectionUtils.isNotEmpty(fields)) {
            Field field = fields.get(0);
            TableId tableId = field.getAnnotation(TableId.class);
            idType = tableId.type();
            columnName = tableId.value();
            if (StringUtils.isNotBlank(columnName)) {
                boolean annotationTableFieldPresent = field.isAnnotationPresent(TableField.class);
                if (annotationTableFieldPresent) {
                    TableField tableFieldAnn = field.getAnnotation(TableField.class);
                    if (tableFieldAnn.exist()) {
                        columnName = tableFieldAnn.value();
                    }
                }
            }
            if (StringUtils.isBlank(columnName)) {
                columnName = field.getName();
            }
        }
        metadataModel.setIdType(idType);
        metadataModel.setIdColumnName(columnName);
    }

    private String getParentIdColumName(Class<?> cls){
        Map<String, ParentIdColumn> parentIdColumnNamesAndParentClassMap = metadataModel.getParentIdColumnNamesAndParentClassMap();
//        String columnName = "";
        List<Field> fields = FieldInvokeUtil.findFieldsByAnnotationPresent(cls, ParentId.class);
        if (CollectionUtils.isNotEmpty(fields)) {
            for (int i = 0; i < fields.size(); i++) {
                Field field =  fields.get(i);
                String columnName = "";
                boolean annotationTableFieldPresent = field.isAnnotationPresent(TableField.class);
                if (annotationTableFieldPresent) {
                    TableField tableFieldAnn = field.getAnnotation(TableField.class);
                    if (tableFieldAnn.exist()) {
                        columnName = tableFieldAnn.value();
                    }
                }
                if (StringUtils.isBlank(columnName)) {
                    columnName = field.getName();
                }
                ParentId parentIdAnnotation = field.getAnnotation(ParentId.class);
                boolean sqlWhereWithOtherParentIds = parentIdAnnotation.sqlWhereWithOtherParentIds();
                ParentIdColumn parentIdColumn = new ParentIdColumn();
                parentIdColumn.setColumnName(columnName);
                parentIdColumn.setParentClass(parentIdAnnotation.parentClass());
                parentIdColumn.setRoleSqlBuilder(parentIdAnnotation.sqlBuildClass());
                parentIdColumn.setSqlWhereWithOtherParentIds(sqlWhereWithOtherParentIds);
                parentIdColumnNamesAndParentClassMap.put(columnName,parentIdColumn);
            }
        }
        return "";
    }

    private void getParentIdColumNames(Class<?> cls,MetadataModel metadataModel){
        List<Field> fields = FieldInvokeUtil.findFieldsByAnnotationPresent(cls, ParentId.class);
        if (CollectionUtils.isNotEmpty(fields)) {
//            List<String> inColumnNames = Lists.newArrayListWithCapacity(0);
//            List<String> outColumnNames = Lists.newArrayListWithCapacity(0);
            List<String> inColumnNames = metadataModel.getInsideParentIdColumnNames();
            List<String> outColumnNames = metadataModel.getOutsideParentIdColumnNames();

            Map<String,String> inColumnNamesMap = metadataModel.getInsideParentIdColumnNamesMap();
            Map<String,String> outColumnNamesMap = metadataModel.getOutsideParentIdColumnNamesMap();

            for (int i = 0; i < fields.size(); i++) {
                String columnName = "";
                Field field =  fields.get(i);
                ParentId annotationParentId = field.getAnnotation(ParentId.class);
                boolean annotationTableFieldPresent = field.isAnnotationPresent(TableField.class);
                if (annotationTableFieldPresent) {
                    TableField tableFieldAnn = field.getAnnotation(TableField.class);
                    if (tableFieldAnn.exist()) {
                        columnName = tableFieldAnn.value();
                    }
                }
                if (StringUtils.isBlank(columnName)) {
                    columnName = annotationParentId.value();
                }

                String fieldName = field.getName();
                if (StringUtils.isBlank(columnName)) {
                    columnName = fieldName;
                }
                ParentIdType parentIdType = annotationParentId.type();
                if(ParentIdType.INSIDE == parentIdType){
                    inColumnNames.add(columnName);
                    inColumnNamesMap.put(columnName,fieldName);
                } else if(ParentIdType.OUTSIDE == parentIdType){
                    outColumnNames.add(columnName);
                    outColumnNamesMap.put(columnName,fieldName);
                }
            }
        }
    }

    private void parseColumNames(Class<?> cls, MetadataModel metadataModel){
        String tableName = metadataModel.getTableName();
        Field[] declaredFields = cls.getDeclaredFields();
//        declaredFields;
//        List<Field> fields = cls.getDeclaredFields();
        List<Field> fields = Lists.newArrayListWithCapacity(0);
        List<Field> roleChildrenFields = metadataModel.getRoleChildrenFields();

        for (int i = 0; i < declaredFields.length; i++) {
            Field declaredField = declaredFields[i];
            boolean roleChildrenAnnotationPresent = declaredField.isAnnotationPresent(RoleChildren.class);
            if (roleChildrenAnnotationPresent) {
                RoleChildren annotation = declaredField.getAnnotation(RoleChildren.class);
                if (annotation.exist()) {
                    roleChildrenFields.add(declaredField);
                }
            }
            JJFieldType jjFieldType = JJFieldType.javaType(declaredField.getType().getName());
            if (jjFieldType != null) {
                fields.add(declaredField);
            }
        }

        if (CollectionUtils.isNotEmpty(fields)) {
            List<String> columnNames = metadataModel.getColumnNames();
            Map<String,String> columnNamesMap = metadataModel.getColumnNamesMap();

            List<String> indexColumnNames = metadataModel.getIndexColumnNames();
            Map<String,String> indexColumnNamesMap = metadataModel.getIndexColumnNamesMap();
            Map<String, FlyTableColumn> columnNamesStructureMap = metadataModel.getColumnNamesStructureMap();
            boolean isExsitJsonType = false;
            for (int i = 0; i < fields.size(); i++) {
                String columnName = "";
                Field field =  fields.get(i);
                boolean annotationFlyIndexPresent = field.isAnnotationPresent(FlyIndex.class);
                boolean annotationTableFieldPresent = field.isAnnotationPresent(TableField.class);
                if (annotationTableFieldPresent) {
                    TableField tableFieldAnn = field.getAnnotation(TableField.class);
                    if (tableFieldAnn.exist()) {
                        columnName = tableFieldAnn.value();
                    }
                }
//                if (StringUtils.isBlank(columnName)) {
//                    columnName = annotationParentId.value();
//                }

                String fieldName = field.getName();
                if (StringUtils.isBlank(columnName)) {
                    columnName = fieldName;
                }
                //关键字检查
                if (SqlUtil.keywordCheck(columnName)) {
                    columnName = "`" + columnName + "`";
                }
                columnNames.add(columnName);
                columnNamesMap.put(columnName,fieldName);
                if (annotationFlyIndexPresent) {
                    FlyIndex flyIndex = field.getAnnotation(FlyIndex.class);
                    String indexName = flyIndex.name();
                    if (StringUtils.isBlank(indexName)) {
                        indexName = "index_" + tableName + "_" + columnName;
                    }
                    //TODO desgin all about info save pojo
                    indexColumnNames.add(indexName);
                    indexColumnNamesMap.put(indexName,columnName);
                }

                Class<?> type = field.getType();
                if (!isExsitJsonType) {
                    isExsitJsonType = isExsitJsonType(type);
                }

                FlyTableColumn flyTableColumn = new FlyTableColumn();
                flyTableColumn.setName(columnName);
                flyTableColumn.setJavaName(fieldName);
                JJFieldType jjFieldType = JJFieldType.javaType(field.getType().getName());
                String jdbcType = jjFieldType.getJdbcType();
                flyTableColumn.setType(jdbcType);
                int length = 0;
                if (JDBCType.VARCHAR.getType().equals(jdbcType)) {
                    length = 255;
                } else if (JDBCType.INT.getType().equals(jdbcType)) {
                    length = 11;
                }  else if (JDBCType.INTEGER.getType().equals(jdbcType)) {
                    length = 11;
                }
                flyTableColumn.setLength(length);
                columnNamesStructureMap.put(columnName, flyTableColumn);
            }
            metadataModel.setIsExsitJsonType(isExsitJsonType);
            if (CollectionUtils.isNotEmpty(columnNames)) {
//                StringBuffer selectSql = new StringBuffer("select");
                StringBuffer columnNamesStr = new StringBuffer("");
                for (int i = 0; i < columnNames.size(); i++) {
                    String columnName =  columnNames.get(i);
                    columnNamesStr.append(columnName).append(",");
                }
                String columnNamesString = columnNamesStr.toString();
                if (columnNamesString.endsWith(",")) {
                    columnNamesString = columnNamesString.substring(0,columnNamesString.length()-1);
                }
//                selectSql.append(columnNamesString).append(" from ").append(tableName);
                metadataModel.setColumnNamesString(columnNamesString);
            }
        }
    }



    private boolean isAutoResultMap(Class<?> cls) {
        boolean flag = false;
        if (cls.isAnnotationPresent(TableName.class)) {
            TableName annotation = cls.getAnnotation(TableName.class);
            return annotation.autoResultMap();
        }
        return flag;
    }

    private boolean isExsitJsonType(Class<?> cls) {
        boolean flag = false;
        String className = cls.getName();
        if ("com.alibaba.fastjson.JSONArray".equals(className)) {
            flag = true;
        } else if ("com.alibaba.fastjson.JSONObject".equals(className)) {
            flag = true;
        }
        return flag;
    }

//    private void parseRelated(Class<?> cls,MetadataModel metadataModel){
//        String tableName = metadataModel.getTableName();
////        String idColumnName = metadataModel.getIdColumnName();
////        boolean annotationPresent = cls.isAnnotationPresent(RelatedRole.class);
////        if (annotationPresent) {
////            RelatedRole relatedRole = cls.getAnnotation(RelatedRole.class);
////            String related = relatedRole.related();
////            Class<? extends FlyBasicEntity> relatedModelClass = relatedRole.modelClass();
////
////            RelatedMetadataModel RelatedMetadataModel = new RelatedMetadataModel();
////            RelatedMetadataModel.setMainClass(cls);
////            RelatedMetadataModel.setRelatedClass(relatedModelClass);
////            RelatedMetadataModel.setRelated(related);
////            RelatedMetadataModel.setIdColumnName(idColumnName);
//////            RelatedMetadataModel.setRelatedIdColumnName();
////
////        }
//
//        Field[] declaredFields = cls.getDeclaredFields();
//        List<Field> fields = Lists.newArrayListWithCapacity(0);
//
//        for (int i = 0; i < declaredFields.length; i++) {
//            Field declaredField = declaredFields[i];
//            JJFieldType jjFieldType = JJFieldType.javaType(declaredField.getType().getName());
//            if (jjFieldType != null) {
//                fields.add(declaredField);
//            }
//        }
//
//        if (CollectionUtils.isNotEmpty(fields)) {
////            List<String> columnNames = metadataModel.getColumnNames();
//            Map<String, String> columnNamesMap = metadataModel.getColumnNamesMap();
////
////            List<String> indexColumnNames = metadataModel.getIndexColumnNames();
////            Map<String,String> indexColumnNamesMap = metadataModel.getIndexColumnNamesMap();
////            Map<String, FlyTableColumn> columnNamesStructureMap = metadataModel.getColumnNamesStructureMap();
//            boolean isExsitJsonType = false;
//            for (int i = 0; i < fields.size(); i++) {
//                String columnName = "";
//                Field field = fields.get(i);
//                boolean relatedRoleAnnotationPresent = field.isAnnotationPresent(RelatedRole.class);
//                if (relatedRoleAnnotationPresent) {
//                    continue;
//                }
//                RelatedRole relatedRole = field.getAnnotation(RelatedRole.class);
//                String related = relatedRole.related();
//                Class<? extends FlyBasicEntity> relatedModelClass = relatedRole.modelClass();
//                RelatedMetadataModel relatedMetadataModel = new RelatedMetadataModel();
////                relatedMetadataModel.setMainClass(cls);
////                relatedMetadataModel.setRelatedClass(relatedModelClass);
////                relatedMetadataModel.setRelated(related);
////                String mainColumnName = getColumnName(field);;
////                relatedMetadataModel.setMainColumnName(mainColumnName);
//
//
////                MetadataModelCache metadataModelCache = SpringContextUtil.getBean("metadataModelCache");
////                MetadataModel relatedModelClassMetadataModel = metadataModelCache.get(relatedModelClass.getName());
////                RelatedMetadataModel relatedMetadataModel2 = new RelatedMetadataModel();
////                relatedMetadataModel2.setMainClass(relatedModelClass);
////                relatedMetadataModel2.setRelatedClass(cls);
////                relatedMetadataModel2.setRelated(related);
////                relatedMetadataModel2.setMainColumnName();
////                relatedMetadataModel2.setRelatedColumnName();
//            }
//        }
//    }
//
//    private String getColumnName(Field field){
//        String columnName = "";
//        boolean annotationTableFieldPresent = field.isAnnotationPresent(TableField.class);
//        if (annotationTableFieldPresent) {
//            TableField tableFieldAnn = field.getAnnotation(TableField.class);
//            if (tableFieldAnn.exist()) {
//                columnName = tableFieldAnn.value();
//            }
//        }
//        String fieldName = field.getName();
//        if (StringUtils.isBlank(columnName)) {
//            columnName = fieldName;
//        }
//        //关键字检查
//        if (SQLUtil.keywordCheck(columnName)) {
//            columnName = "`" + columnName + "`";
//        }
//        return columnName;
//    }

}
